#include "gtest/gtest.h"

#include <functional>

int f1(int arg){
    return arg;
}


class Object {
public:
    int memfn(int arg){
        return arg;
    }
};

TEST(FuntionTest, use){
    {
        std::function<int (int)> callback = f1;
        ASSERT_TRUE(callback != nullptr);
        ASSERT_EQ(10, callback(10));
    }

    {
        Object o;
        std::function<int (int)> callback = std::bind(&Object::memfn, &o, std::placeholders::_1);
        ASSERT_TRUE(callback != nullptr);
        ASSERT_EQ(10, callback(10));
    }

    std::function<int (int)> callback;
    {
        ASSERT_TRUE(callback == nullptr);
    }
}


#include <functional>
class IBreakPointSwitchState {
public:
    IBreakPointSwitchState(){}
    virtual ~IBreakPointSwitchState(){}

    typedef void * ContextPtr;
    typedef std::function<int (ContextPtr)> Callback;

    virtual int toEnd(Callback beforeSwitch, ContextPtr ctx) = 0;
    virtual int toInited(Callback beforeSwitch, ContextPtr ctx) = 0;
    virtual int toConnected(Callback beforeSwitch, ContextPtr ctx) = 0;
    virtual int toLogined(Callback beforeSwitch, ContextPtr ctx) = 0;
    virtual int toDisconnected(Callback beforeSwitch, ContextPtr ctx) = 0;
};

class BreakPointSwitchState : public IBreakPointSwitchState{
public:
    virtual ~BreakPointSwitchState(){}
    int toEnd(Callback beforeSwitch, ContextPtr ctx) override {
        if(0 != callBefore(beforeSwitch, ctx)){
            return -1;
        }
        //other
        return 0;
    }
    int toInited(Callback beforeSwitch, ContextPtr ctx) override{
        if(0 != callBefore(beforeSwitch, ctx)){
            return -1;
        }
        //other
        return 0;
    }
    int toConnected(Callback beforeSwitch, ContextPtr ctx) override{
        if(0 != callBefore(beforeSwitch, ctx)){
            return -1;
        }
        //other
        return 0;
    }
    int toLogined(Callback beforeSwitch, ContextPtr ctx) override{
        if(0 != callBefore(beforeSwitch, ctx)){
            return -1;
        }
        //other
        return 0;
    }

    int toDisconnected(Callback beforeSwitch, ContextPtr ctx) override{
        if(0 != callBefore(beforeSwitch, ctx)){
            return -1;
        }
        //other
        return 0;
    }
private:
    int callBefore(Callback beforeSwitch, ContextPtr ctx){
        return (beforeSwitch != nullptr) ? beforeSwitch(ctx) : 0;
    }
};

#include <iostream>
class Service {
public:
    Service() 
    :state(new BreakPointSwitchState)
    {
    }
    ~Service()
    {
        if(state != nullptr){
            delete state;
        }
    }

    static int beforeToEndStatic(void * ptr){
        Service * service = static_cast<Service *>(ptr);
        service->beforeToEnd(service);
        std::cout << "before state switch in static func" << std::endl;
        return 0;
    }
    
    int beforeToEnd(void * ptr){ 
        std::cout << "before state switch" << std::endl;
        return 0;
    }

    int toEnd() {

        state->toEnd(std::bind(&Service::beforeToEnd, this, std::placeholders::_1), this);

        state->toEnd(std::bind(beforeToEndStatic, std::placeholders::_1), this);

        std::string str = "outside tmp var on stack";
        std::cout << "str: " << str << std::endl;
        state->toEnd(
            [&str] (void * ptr) int { 
                std::cout <<"lambda call, args:" << str  << std::endl;
                str += "|new content from lamdba";
                return 0;
            }
            , this);
        std::cout << "str was changed in lambda:" << str << std::endl;
        return 0;
    }

private:
    IBreakPointSwitchState * state;
};

TEST(IBreakPointStateTest, use){
    Service service;
    service.toEnd();
}
